package com.arykow.engine.onwebapp.server;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.Charset;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import net.sf.json.JSON;
import net.sf.json.xml.XMLSerializer;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;

public abstract class AbstractSourceTransformer {
	private final Charset charset = Charset.forName("UTF-8");

	private final void transform(Source source, OutputStream outputStream) {
		try {
			String filename = String.format("%s.xml", getClass().getName().replaceAll("\\.", "/"));
			InputStream stream = getClass().getClassLoader().getResourceAsStream(filename);
			TransformerFactory tf = TransformerFactory.newInstance();
			Transformer transformer = tf.newTransformer(new StreamSource(stream));
			transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
			transformer.setOutputProperty(OutputKeys.ENCODING, charset.name());
			transformer.transform(source, new StreamResult(outputStream));
			stream.close();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public final SourceTransformResult apply(Source source) {
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		try {
			transform(source, outputStream);
			outputStream.close();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return new SourceTransformResultImpl(outputStream.toByteArray());
	}

	private final class SourceTransformResultImpl implements SourceTransformResult {
		private final XMLSerializer serializer = new XMLSerializer();
		private final byte[] bytes;

		private SourceTransformResultImpl(byte[] bytes) {
			super();
			this.bytes = bytes;
		}

		public final void write(OutputStream outputStream) {
			try {
				OutputStreamWriter writer = new OutputStreamWriter(outputStream, charset);
				write(writer);
				writer.close();
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}

		public final void write(Writer writer) {
			try {
				Reader reader = asReader();
				IOUtils.copy(reader, writer);
				reader.close();
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}

		public final Reader asReader() {
			return new InputStreamReader(new ByteArrayInputStream(bytes));
		}

		public final JSON asJSON() {
			JSON result = null;
			try {
				StringWriter writer = new StringWriter();
				IOUtils.copy(asReader(), writer);
				result = serializer.read(writer.toString());
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
			return result;
		}
	}

}
